
#include "appXssl.h"

#if defined(SSL)
#include "polarssl/config.h"

#include "polarssl/net.h"
#include "polarssl/ssl.h"
#include "polarssl/entropy.h"
#include "polarssl/ctr_drbg.h"
#include "polarssl/error.h"
#include "polarssl/certs.h"
#endif

#define STUN_PORT_NUM       3478

xsCtx_t 		        s_stxsCtx;
extern xsMsg_t      s_stMsg;
extern xsUserCfg_t  s_stUserCfg;

#if defined(SSL)
ssl_context g_sslCtx;
unsigned char g_ucSSLDebugLevel = 1;
#endif

unsigned char g_ucXcStep = P_INIT;
unsigned char g_ucUDPCheckStep = P2P_SYN_CKECK;
unsigned char g_ucTcpErrCount = 0;
extern unsigned char g_ucGprsNewStep;
extern unsigned char g_ucPreStat;
extern unsigned char g_ucCmdStat;

unsigned char s_ucXcSBuf[XC_TCP_BUF_SIZE] = {0};
unsigned char s_ucXcRBuf[XC_TCP_BUF_SIZE] = {0};

U8 xc_fd = 0xFF;
xsNetSR_t g_stTCPRbuf;

#if defined(XC_UDP)
U8 p2p_fd = 0xFF;
xsNetSR_t g_stUDPRbuf;
xsNatINfo_t stNatInfo;
xsNatAttr_t s_stxsNatAttr;
char s_acUDPBuf[12] = {0};
#endif

unsigned char s_ucDnsStat = SWITCH_OFF;
static U8   s_aucXSIP[4] = {0};

unsigned int tcp_buff_in(unsigned char *pucInData, unsigned int uiLen)
{
  unsigned int uiIn = 0;
  if(!pucInData){
    return 0;
  }
  //print_log(D_INFO, ("TCP in %d\n", uiLen));
  if(XS_TCP_RBUF_SIZE == g_stTCPRbuf.uiRILen+1){
      g_stTCPRbuf.uiROLen = 0;
  }
  if (g_stTCPRbuf.uiRILen+1 == g_stTCPRbuf.uiROLen) {
    print_log(D_ERROR, ("TCP buffer is full [%d/%d]\n", g_stTCPRbuf.uiRILen, g_stTCPRbuf.uiROLen));
    return 0;
  }

  for(uiIn=0; uiIn<uiLen; uiIn++){
    g_stTCPRbuf.cRBuf[g_stTCPRbuf.uiRILen] = pucInData[uiIn];
    g_stTCPRbuf.uiRILen++;
    if(XS_TCP_RBUF_SIZE == g_stTCPRbuf.uiRILen){
      print_log(D_INFO, ("Max TCP buffer %d\n", g_stTCPRbuf.uiRILen));
      g_stTCPRbuf.uiRILen = 0;
    }
    if (g_stTCPRbuf.uiRILen+1 == g_stTCPRbuf.uiROLen) {
      print_log(D_ERROR, ("TCP buffer is full [%d/%d]\n", g_stTCPRbuf.uiRILen, g_stTCPRbuf.uiROLen));
      break;
    }
  }

  return uiIn;
}

unsigned int tcp_buff_out(unsigned char *pucOutData, unsigned int uiSize)
{
  unsigned int uiOut = 0;
  if(!pucOutData){
    return 0;
  }
  
  if(g_stTCPRbuf.uiRILen == g_stTCPRbuf.uiROLen){
    return 0;
  }
  
  for(uiOut=0; uiOut<uiSize; ){
    pucOutData[uiOut] = g_stTCPRbuf.cRBuf[g_stTCPRbuf.uiROLen];
    uiOut++;
    g_stTCPRbuf.uiROLen++;
    if(XS_TCP_RBUF_SIZE == g_stTCPRbuf.uiROLen){
      g_stTCPRbuf.uiROLen = 0;
    }
    if(g_stTCPRbuf.uiRILen == g_stTCPRbuf.uiROLen){
      break;
    }
  }
  //print_log(D_INFO, ("TCP out %d[%d/%d]\n", uiSize, g_stTCPRbuf.uiROLen, g_stTCPRbuf.uiRILen));
  return uiOut;
}

int my_tcp_connect(uint32_t uiIP)
{
	int iRet = -1;
	switch (tcp_get_state(xc_fd)) {
    case TCP_STATE_FREE:
    case TCP_STATE_CLOSED:
      tcp_connect (xc_fd, s_aucXSIP, s_stUserCfg.usPost, 0);
			print_log(D_INFO, ("Connect to %d.%d.%d.%d:%d ...\n",\
							s_aucXSIP[0], s_aucXSIP[1], s_aucXSIP[2],s_aucXSIP[3],s_stUserCfg.usPost));
      break;
		case TCP_STATE_CONNECT:
			iRet = 0;
			break;
  }
	return iRet;
}

U16 xc_tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par) 
{
	/* This function is called by the TCP module on TCP event */
  /* Check the Net_Config.h for possible events.            */
  /* Make a reference to suppress compiler warnings. */
	switch (evt) {
		case TCP_EVT_CONREQ:
			/* Remote host is trying to connect to our TCP socket. */
			/* 'ptr' points to Remote IP, 'par' holds the remote port. */
			/* Return 1 to accept connection, or 0 to reject connection */
			print_log(D_ERROR, ("\nTCP_EVT_CONREQ:%d\n", evt));
			break;
		case TCP_EVT_ABORT:
			/* Connection was aborted */
      g_ucXcStep = P_RELEASE;
      if(3 == g_ucTcpErrCount){
        g_ucGprsNewStep = G_SHOP_PPP;
        g_ucTcpErrCount = 0;
      }
      g_ucTcpErrCount++;
			print_log(D_ERROR, ("\nTCP_EVT_ABORT:%d\n", evt));
			break;
		case TCP_EVT_CONNECT: // == 1			
			/* Socket is connected to remote peer. */
			break;
		case TCP_EVT_CLOSE: // == 2
			/* Connection has been closed */
			g_ucXcStep = P_RELEASE;
      if(3 == g_ucTcpErrCount){
        g_ucGprsNewStep = G_SHOP_PPP;
        g_ucTcpErrCount = 0;
      }
      g_ucTcpErrCount++;
			print_log(D_ERROR, ("\nTCP_EVT_CLOSE:%d\n", evt));
			break;
		case TCP_EVT_ACK: // == 4
			/* Our sent data has been acknowledged by remote peer */
			break;
		case TCP_EVT_DATA:// == 5
			/* TCP data frame has been received, 'ptr' points to data */
			/* Data length is 'par' bytes */
      tcp_buff_in(ptr, par);
			break;
		default:
			print_log(D_ERROR, ("\nevt:%d\n",evt));
			break;
	}
	return (0);
}

int my_check_tcp_sta(void)
{
  int iRet = XS_CONTINUE;
  if(0 < xc_fd){
    if(TCP_STATE_CONNECT == tcp_get_state(xc_fd)){
      iRet = XS_OK;
    }
  }else{
    iRet = XS_FAILURE;
  }
	return iRet;
}

int TcpSend(unsigned char ucSSLFlag, const unsigned char *pucSData, unsigned int uiLen)
{
	int iRet  = XS_TCP_BUSY; // -1 it is mean SOCKET busy !
	U8 *pucSBuf = 0;
  if(XS_SSL_ENABLE != ucSSLFlag){
    if (tcp_check_send (xc_fd) == __TRUE) {
      pucSBuf = tcp_get_buf(uiLen);
  		if(pucSBuf){
  	    memcpy(pucSBuf, pucSData, uiLen);
  	    iRet = tcp_send (xc_fd, pucSBuf, uiLen);
  			if(__TRUE == iRet){
  				iRet = uiLen;
  				//print_log(D_DEBUG, ("TCP S[%d]:%s\n",iRet, pucSData));
  			}else{
          iRet  = XS_TCP_BUSY;
        }
  		}else{
  			print_log(D_ERROR, ("TCP MEM Err\n"));
        iRet  = XS_MEM_ERR;
  		}
    }
  }
  #if defined(SSL)
  else {
    
    iRet = ssl_write( &g_sslCtx, pucSData, uiLen);
    if(iRet <= 0 ){        
      if( iRet != POLARSSL_ERR_NET_WANT_READ && iRet != POLARSSL_ERR_NET_WANT_WRITE ){
        print_log(D_ERROR, ( " failed\n  ! ssl_write returned %d\n\n", iRet ));
        iRet  = XS_FAILURE;
      }else{
        iRet  = XS_TCP_BUSY;
      }
    }
    if(0 < iRet){
      print_log(D_DEBUG, ( "%s\n",(char *) pucSData ));
    }
  }
  #endif
	return iRet;
}

int TcpRecv(unsigned char ucSSLFlag, unsigned char *pucRData, unsigned int uiSize)
{
	int iRet  = XS_TCP_BUSY;
  if(XS_SSL_ENABLE != ucSSLFlag){
    iRet = tcp_buff_out(pucRData, uiSize);
    if(0 < iRet){
      //print_log(D_DEBUG, ("TCP R[%d]:%s\n",iRet, pucRData));
    }else{
      iRet  = XS_TCP_BUSY;
    }
  }
  #if defined(SSL)
  else {
    iRet = ssl_read( &g_sslCtx, pucRData, uiSize);
    if(iRet <= 0 ){        
      if( iRet != POLARSSL_ERR_NET_WANT_READ && iRet != POLARSSL_ERR_NET_WANT_WRITE ){
        print_log(D_ERROR, ( "failed\n  ! ssl_read returned %d\n\n", iRet ));
        iRet  = XS_FAILURE;
      }else{
        iRet  = XS_TCP_BUSY;
      }
    }
    if(0 < iRet){
      print_log(D_DEBUG, ( "%s\n",(char *) pucRData));
    }
  }
  #endif
	return iRet;
}

#if defined(XC_UDP)
int my_udp_connect(void)
{
	if (p2p_fd != 0) {
		/* Open UDP port 1000 for communication */
		udp_open (p2p_fd, 1000);
	}
	return 0;
}

U16 p2p_udp_callback (U8 soc, U8 *rip, U16 rport, U8 *buf, U16 len) 
{
	g_stUDPRbuf.uiRILen = 0;
	if(XS_TCP_BUF_SIZE > len){
		memcpy(g_stUDPRbuf.cRBuf, buf, len);
		if(stNatInfo.usMapPort != rport){
			stNatInfo.usMapPort = rport;
		}
		g_stUDPRbuf.uiRILen = len;
	}
  return (0);
}

int UdpSend(unsigned int uiDstIP, unsigned short usDstPort, const unsigned char *pucSData, unsigned int uiLen)
{
	int iRet  = -1;
	U8  IP[4] = {10,78,28,16};
	U8 *pucSBuf = 0;
	
	IP[0] = (uiDstIP&0xFF000000)>>24;
	IP[1] = (uiDstIP&0x00FF0000)>>16;
	IP[2] = (uiDstIP&0x0000FF00)>>8;
	IP[3] = (uiDstIP&0x000000FF)>>0;
	
	pucSBuf = udp_get_buf (uiLen);
	if(pucSBuf){
		memset(pucSBuf, 0, uiLen);
		memcpy(pucSBuf, pucSData, uiLen);
		udp_send (p2p_fd, IP, usDstPort, pucSBuf, uiLen);
		iRet = 0;
		print_log(D_DEBUG, ("\nUDP Send to %d.%d.%d.%d:%d \n",IP[0], IP[1], IP[2],IP[3],usDstPort));
	}else{
		print_log(D_ERROR, ("\nTCP MEM Err\n"));
	}
	return iRet;
}

int UdpRecv(unsigned int uiDstIP, unsigned short usDstPort, unsigned char *pucBuf, unsigned int uiSize)
{
	int iRet  = 0;

	if(0 < g_stUDPRbuf.uiRILen){
		if(g_stUDPRbuf.uiRILen < uiSize){
			memcpy(pucBuf, g_stUDPRbuf.cRBuf, g_stUDPRbuf.uiRILen);
			iRet = g_stUDPRbuf.uiRILen;
			g_stUDPRbuf.uiRILen = 0;
			print_log(D_DEBUG, ("\nUDP RECV:%s\n", pucBuf));
		}else{
			iRet  = -1;
		}
	}
	return iRet;
}

int parse_nat_info(xsNatINfo_t *pstNatInfo, unsigned char *pucInfo, unsigned char ucLen)
{
	unsigned char ucPos = 0;
	unsigned char ucBuf[9] = {0}, uci = 0, ucCh = 0, ucPara = 0;
	if(!pstNatInfo || !pucInfo){
		return -1;
	}
	memset(pstNatInfo, 0, sizeof(xsNatINfo_t));
	while(ucPos != ucLen){
		ucCh = pucInfo[ucPos];
		ucPos++;
		if('|' != ucCh){
			ucBuf[uci] = ucCh;
			uci++;
			continue;
		}else{
			ucPara++;
			uci = 0;
		}
		switch(ucPara){
			case 1:
				pstNatInfo->ucType = atoi((const char *)ucBuf);
				memset(ucBuf, 0, 9);
				break;
			case 2:				
				pstNatInfo->uiMapHexIp = atoi((const char *)ucBuf);
				memset(ucBuf, 0, 9);
				break;
			case 3:
				pstNatInfo->usMapPort = atoi((const char *)ucBuf);
				break;
		}
	}
	return 0;
}
#endif

static void dns_cbfunc (unsigned char event, unsigned char *ip) {
  /* This function is called by the DNS Client when dns event occurs. */

  switch (event) {
    case DNS_EVT_SUCCESS:
      /* Host Address successfully resolved. When IP address is already */
      /* cached, there is no DNS Request sent to remote DNS Server.     */
      print_log(D_INFO, (" %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]));
      s_aucXSIP[0] = ip[0];
      s_aucXSIP[1] = ip[1];
      s_aucXSIP[2] = ip[2];
      s_aucXSIP[3] = ip[3];
      s_ucDnsStat = SWITCH_ON;
      break;

    case DNS_EVT_NONAME:
      /* Host Name does not exist in DNS record database. */
      print_log(D_ERROR, ("Host name does not exist.\n"));
      break;

    case DNS_EVT_TIMEOUT:
      /* All DNS Resolver retries used up and timeouts expired. */
      print_log(D_ERROR, ("DNS Resolver Timeout expired, Host Address not resolved.\n"));
      break;

    case DNS_EVT_ERROR:
      /* DNS Protocol Error, invalid or corrupted reply received. */
      print_log(D_ERROR, ("DNS Resolver Protocol Error, Host Address not resolved.\n"));
      return;
  }
}

static void tcp_and_udp_init(void)
{
  /* Get socket resource , Can get how many , see Net_Config*/
	if(0xFF == xc_fd){
		xc_fd = tcp_get_socket (TCP_TYPE_CLIENT, 0, 65500, xc_tcp_callback);
	}
  #if defined(XC_UDP)
	if(0xFF == p2p_fd){
		p2p_fd = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, p2p_udp_callback);
	}
  #endif
}

static void xc_socket_close(void)
{
	if(0xFF != xc_fd){
		tcp_close (xc_fd);
    xc_fd = 0xFF;
	}
  #if defined(XC_UDP)
	if(0xFF != p2p_fd){
		udp_close (p2p_fd);
    p2p_fd = 0xFF;
	}
  #endif
}

#if defined(SSL)
void ssl_debug( void *ctx, int level, const char *str )
{
	if(g_ucSSLDebugLevel <= level)
		print_log(D_DEBUG, ("%s", str));
}

int ssl_net_send( void *ctx, const unsigned char *buf, size_t len )
{
  int iRet  = -1; // -1 it is mean SOCKET busy !
	U8 *pucSBuf = 0;
  if (tcp_check_send (xc_fd) == __TRUE) {
    pucSBuf = tcp_get_buf(len);
		if(pucSBuf){
	    memcpy(pucSBuf, buf, len);
	    iRet = tcp_send (xc_fd, pucSBuf, len);
			if(__TRUE == iRet){
				iRet = len;
        g_ucSSLDebugLevel = 1;
			}
		}else{
      iRet = POLARSSL_ERR_NET_SEND_FAILED;
      print_log(D_ERROR, ("SSL S memory error\n"));
		}
  }else{
    g_ucSSLDebugLevel = 5;
    iRet = POLARSSL_ERR_NET_WANT_WRITE;
  }
	return iRet;
}

int ssl_net_recv( void *ctx, unsigned char *buf, size_t len )
{ 
  int iRet  = 0;
  
	iRet = tcp_buff_out(buf, len);
  if(0 == iRet){
    g_ucSSLDebugLevel = 3;
    iRet = POLARSSL_ERR_NET_WANT_READ;
  }else{
    g_ucSSLDebugLevel = 1;
  }
	return iRet;
}

entropy_context entropy;
ctr_drbg_context ctr_drbg;

int ssl_ctx_init(void)
{
	int iRet;
	static int iInitFlag = 0;
  
 	if(1 == iInitFlag){
		return 0;
	}

  print_log(D_INFO, ("\n  . Seeding the random number generator..."));
  entropy_init( &entropy );
  iRet = ctr_drbg_init( &ctr_drbg, entropy_func, &entropy,
                             (unsigned char *) s_stUserCfg.acDomain, strlen( s_stUserCfg.acDomain ) );
  
  if( 0 != iRet){
    print_log(D_INFO,( " failed\n  ! ctr_drbg_init returned 0x%X\n", -iRet));
    goto exit;
  }
  print_log(D_INFO,( " ok\n" ));

	print_log(D_INFO,( "  . Setting up the SSL/TLS structure..." ));
  iRet = ssl_init( &g_sslCtx );
  if( 0 != iRet ){
    print_log(D_INFO,( " failed\n  ! ssl_init returned 0x%X\n", -iRet));
    goto exit;
  }
  print_log(D_INFO,( " ok\n" ));
  
  ssl_set_endpoint( &g_sslCtx, SSL_IS_CLIENT );
  ssl_set_authmode( &g_sslCtx, SSL_VERIFY_NONE);
	ssl_set_hostname( &g_sslCtx, s_stUserCfg.acDomain);

	ssl_set_min_version( &g_sslCtx, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1 );
	ssl_set_max_version( &g_sslCtx, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1 );
		
  ssl_set_rng( &g_sslCtx, ctr_drbg_random, &ctr_drbg);
  ssl_set_dbg( &g_sslCtx, ssl_debug, 0);
  ssl_set_bio( &g_sslCtx, ssl_net_recv, 0, ssl_net_send, 0);
  iInitFlag = 1;
	exit:
		return iRet;
}

int ssl_conn(void)
{
  int iRet = 0;
  static char cFlag = 0;
  if(0 == cFlag){
    print_log(D_INFO,( "  .TLS handshake...\n" ));
    cFlag = 1;
  }
  
  iRet = ssl_handshake(&g_sslCtx);
  if(0 != iRet){
    if(iRet != POLARSSL_ERR_NET_WANT_READ && iRet != POLARSSL_ERR_NET_WANT_WRITE ){
      print_log(D_INFO,( "  . Handshake failed\n  ! ssl_handshake returned -0x%x\n", -iRet ));
      iRet = XS_SSL_HANDSHAKE_ERR;
    }else{
      iRet = XS_CONTINUE;
    }
    goto exit;
  }
  print_log(D_INFO,( "  . Handshake ok\n" ));

	print_log(D_INFO,( "  . Verifying peer X.509 certificate...\n" ));
  iRet = ssl_get_verify_result( &g_sslCtx );
  if(0 != iRet){
    print_log(D_INFO,( " failed\n" ));

    if( ( iRet & BADCERT_EXPIRED ) != 0 )
        print_log(D_INFO,( "  ! server certificate has expired\n" ));

    if( ( iRet & BADCERT_REVOKED ) != 0 )
        print_log(D_INFO,( "  ! server certificate has been revoked\n" ));

    if( ( iRet & BADCERT_CN_MISMATCH ) != 0 )
        print_log(D_INFO,( "  ! CN mismatch (expected CN=%s)\n", s_stUserCfg.acDomain ));

    if( ( iRet & BADCERT_NOT_TRUSTED ) != 0 )
        print_log(D_INFO, ( "  ! self-signed or not signed by a trusted CA\n" ));

    print_log(D_INFO,( "\n" ));
  }else{
    print_log(D_INFO,( "  . Verified ok\n" ));
  }
	exit:
		return iRet;
}
#endif

static const char acPreStat[][32] = { {"Normal"},
                                  {"Com error"},
                                  {"Data error"},
                                  {"Write flash error"},
                                  {"NO cfg"},
                                  {""},
                                  {""},
                                  {""}};
                                  
extern unsigned int  g_uiTraffic; //Traffic statistics
extern unsigned int  g_uiMaxTraffic;
extern unsigned char g_ucSignal;

char acPreStateBuff[64] = {0};
const char *s_pcRes    = "lomo";

int xc_proc_init(void)
{
  int iRet = 0;
  memset(&s_stxsCtx, 0, sizeof(xsCtx_t));
  memset(&s_stMsg, 0, sizeof(xsMsg_t));
  memset(&g_stTCPRbuf, 0, sizeof(xsNetSR_t));

  #if defined(XC_UDP)
	memset(&s_stxsNatAttr, 0, sizeof(xsNatAttr_t));
  s_stxsCtx.pfUdpSend = UdpSend;
	s_stxsCtx.pfUdpRecv = UdpRecv;
  #endif
	s_stxsCtx.pucRBuf = s_ucXcRBuf;
	s_stxsCtx.uiRSize = XC_TCP_BUF_SIZE;
	s_stxsCtx.pucSBuf = s_ucXcSBuf;
	s_stxsCtx.uiSsize = XC_TCP_BUF_SIZE;

	s_stxsCtx.pcDomain = s_stUserCfg.acDomain;
	s_stxsCtx.pcNode   = s_stUserCfg.acSn;
	s_stxsCtx.pcPwd    = s_stUserCfg.acPwd;
  s_stxsCtx.pcRes    = s_pcRes;
  
	s_stxsCtx.pfTcpSend = TcpSend;
	s_stxsCtx.pfTcpRecv = TcpRecv;
	print_log(D_INFO, ("XC context init"));
  #if defined(SSL)
  iRet = ssl_ctx_init();
  if( 0 != iRet ){
    print_log(D_ERROR, ("  ssl error[%d]\n", iRet));
    return iRet;
  }
  #endif

	iRet = xsInit(&s_stxsCtx);
  if( 0 != iRet ){
    print_log(D_ERROR, ("  failure[%d]\n", iRet));
    return iRet;
  }
  print_log(D_INFO, ("  ok\n"));
  return iRet;
}

int xc_release(void)
{
  g_ucXcStep = P_RELEASE;
  return xsXmppLogout(&s_stxsCtx);
}

int xc_main_proc(unsigned char ucResetFlag)
{
	int iRet = 0;

	static unsigned char  s_ucP2pStat = P2P_CHECKING,
                        s_ucFailCount = 0,
                        s_usLastPerState = 0xFF;

	if(SWITCH_ON == ucResetFlag){
    g_ucXcStep = P_RELEASE;
  }
  
	switch(g_ucXcStep){
		case P_INIT:
			s_ucP2pStat = P2P_CHECKING;
			g_ucUDPCheckStep = P2P_SYN_CKECK;
      if(SWITCH_OFF == s_ucDnsStat){
        g_ucXcStep = P_DNS_REQ;
      }else{
        g_ucXcStep = P_TCP_CONNECT;
        tcp_and_udp_init();
        set_xc_timing_delay(XC_CONNECT_TIMEOUT);    
      }
      
			break;
    case P_DNS_REQ:
      print_log(D_INFO, ("Resolving DNS Host %s\n", s_stUserCfg.acHost));
      iRet = get_host_by_name((U8 *)s_stUserCfg.acHost,dns_cbfunc);
      g_ucXcStep = P_DNS_CHECK;
      set_xc_timing_delay(XC_CONNECT_TIMEOUT);
      break;
    case P_DNS_CHECK:
      if(0 != get_xc_timing_delay()){
        if(SWITCH_ON == s_ucDnsStat){
          buzzer_ring(1);
          g_ucXcStep = P_TCP_CONNECT;
          tcp_and_udp_init();
          set_xc_timing_delay(XC_CONNECT_TIMEOUT);
          print_log(D_INFO, ("  .Resolving DNS ok\n"));
        }
      }else{
        print_log(D_INFO, ("  .Resolving DNS failure\n"));
				s_ucFailCount++;
        if(3 == s_ucFailCount){
          g_ucGprsNewStep = P_RELEASE;
          s_ucFailCount = 0;
        }else{
					g_ucXcStep = P_DNS_REQ;
				}
      }
      break;
		case P_TCP_CONNECT:
			if(SWITCH_ON != s_ucDnsStat){
				g_ucXcStep = P_DNS_REQ;
				break;
			}
      if(0 != get_xc_timing_delay()){
  			iRet = my_tcp_connect(0);
  			if(S_OK == iRet){
  				g_ucXcStep = P_WAIT_CONNECTED;
          set_xc_timing_delay(XC_CONNECT_TIMEOUT);
  			}
      }else{
      	g_ucGprsNewStep = P_RELEASE;
        print_log(D_INFO, ("Connect failure\n"));
      }
			break;
    case P_WAIT_CONNECTED:
      if(0 != get_xc_timing_delay()){
  			if(XS_OK == my_check_tcp_sta()){
  				g_ucXcStep = P_LOGIN;
          s_stxsCtx.ucStep = XC_OPEN_XML;
          set_xc_timing_delay(XC_LOGIN_TIMEOUT);
          print_log(D_INFO, ("XC Launching\n"));
  			}
      }else{
      	g_ucGprsNewStep = P_RELEASE;
        print_log(D_INFO, ("Connect timeout\n"));
      }
      break;
		case P_LOGIN:
      if(0 != get_xc_timing_delay()){
  			iRet = xsXmppLogin(&s_stxsCtx);
        if(XC_TLS_HANDSHAKE == s_stxsCtx.ucStep){
          iRet = ssl_conn();
          if(XS_SSL_HANDSHAKE_ERR == iRet){
            print_log(D_INFO, ("  .XC SSL error\n"));
          }
          if(0 == iRet){
            s_stxsCtx.ucStep = XC_OPEN_STREAM;
            s_stxsCtx.ucNextStep = XC_TLS_AUTH;
          }
        }
        
  			if(XS_XML_LOGIN_SUCCESS == iRet){
          s_ucFailCount = 0;
  				s_ucP2pStat = P2P_FORWARD;
  				g_ucXcStep = P_HEARBEAT;//P_UDP_CONNECT
  				set_xc_timing_delay(0);
          buzzer_ring(3);
  				print_log(D_INFO, ("  .XC Login success\n"));
  			}else if(0 > iRet){
  				g_ucXcStep = P_RELEASE;
          print_log(D_INFO, ("  .XC Login failure\n"));
  			}
      }else{
        s_ucFailCount++;
        if(3 == s_ucFailCount){
          g_ucGprsNewStep = G_SHOP_PPP;
          s_ucFailCount = 0;
        }
        g_ucXcStep = P_LOGOUT;
        print_log(D_INFO, ("  .XC Login timeout\n"));
      }
			break;
      #if defined(XC_UDP)
		case P_UDP_CONNECT:
			iRet = my_udp_connect();
			if(0 > iRet){
				s_ucP2pStat = P2P_FORWARD;
				g_ucXcStep = P_HEARBEAT;
			}else{
				s_stxsNatAttr.uiSrcIp = 	0xFF0000;   //not care if you not care NAT type
				s_stxsNatAttr.uiDstIp = 	0x74D31E02; //0x74D31E02 = 116.211.30.2
				s_stxsNatAttr.usDstPort = STUN_PORT_NUM;
				g_ucXcStep = P_GET_NAT;
			}
			break;
		case P_GET_NAT:
			iRet = xsStunProc(&s_stxsCtx, &s_stxsNatAttr);
			if(XS_OK == iRet){
				s_ucFailCount = 0;
				g_ucXcStep = P_REQ;
			}else if(0 > iRet){
				s_ucP2pStat = P2P_FORWARD;
				g_ucXcStep = P_HEARBEAT;
			}
			break;
		case P_REQ:
			memset(s_stMsg.ucMsgData, 0, sizeof(s_stMsg.ucMsgData));
			sprintf((char *)s_stMsg.ucMsgData, "%d|%d|%d|", s_stxsNatAttr.ucType, s_stxsNatAttr.uiMapIp, s_stxsNatAttr.usMapPort);
			s_stMsg.uiMsgLen = strlen((const char *)s_stMsg.ucMsgData);
			memcpy(s_stMsg.ucJid, s_pcDevPeer, strlen(s_pcDevPeer));
			iRet = xsXmppSendMsg(&s_stxsCtx, &s_stMsg); //s_pcDevPeer
			if(XS_OK == iRet){
				g_ucXcStep = P_GET_REP;
        memset(&s_stMsg, 0, sizeof(xsMsg_t));
				set_xc_timing_delay(4000);
			}
			break;
		case P_GET_REP:
			if(0 != get_xc_timing_delay()){
				iRet = xsXmppRecvMsg(&s_stxsCtx, &s_stMsg);
				if(0 < s_stMsg.uiMsgLen){
					s_ucFailCount = 0;
					g_ucXcStep = P_CHECK;
					parse_nat_info(&stNatInfo, s_stMsg.ucMsgData, s_stMsg.uiMsgLen);
					set_xc_timing_delay(0);
					print_log(D_DEBUG, ("\n\rR:[%s]->[%d]%08X:%d\n\r",s_stMsg.ucJid, stNatInfo.ucType, stNatInfo.uiMapHexIp, stNatInfo.usMapPort));
				}
				if(0 > iRet){
					g_ucXcStep = P_LOGOUT;
				}
			}else{
				if(3 == s_ucFailCount){
					s_ucP2pStat = P2P_FORWARD;
					g_ucXcStep = P_HEARBEAT;
				}else{
					s_ucFailCount++;
					g_ucXcStep = P_REQ;
				}
			}			
			break;
		case P_CHECK:
			if(0 == get_xc_timing_delay()){
					switch(g_ucUDPCheckStep){
					case P2P_SYN_CKECK:
						sprintf(s_acUDPBuf, "%s", P2P_SYN);
						break;
					case P2P_ACK_CKECK:
						sprintf(s_acUDPBuf, "%s", P2P_ACK);
						break;
					case P2P_FIN_CKECK:
						sprintf(s_acUDPBuf, "%s", P2P_FIN);
						break;
				}
				UdpSend(stNatInfo.uiMapHexIp, stNatInfo.usMapPort, (unsigned char *)s_acUDPBuf, strlen((char *)s_acUDPBuf));
				memset(s_acUDPBuf, 0, sizeof(s_acUDPBuf));
				set_xc_timing_delay(4000);
				if(3 == s_ucFailCount){
					s_ucP2pStat = P2P_FORWARD;
					g_ucXcStep = P_HEARBEAT;
				}
				s_ucFailCount++;
			}
			
			iRet = UdpRecv(0, 0, (unsigned char *)s_acUDPBuf, sizeof(s_acUDPBuf));
			if(0 < iRet){
				if(0 == strcmp(s_acUDPBuf, P2P_SYN)){
					g_ucUDPCheckStep = P2P_ACK_CKECK;
				}else if(0 == strcmp(s_acUDPBuf, P2P_ACK)){
					g_ucUDPCheckStep = P2P_FIN_CKECK;
				}else if(0 == strcmp(s_acUDPBuf, P2P_FIN)){
					s_ucP2pStat = P2P_OK;
					g_ucXcStep = P_HEARBEAT;
				}
				set_xc_timing_delay(0);
			}
			
			break;
      #endif
		case P_HEARBEAT:
      if(s_usLastPerState != g_ucPreStat){
        memset(acPreStateBuff, 0, sizeof(acPreStateBuff));
        sprintf(acPreStateBuff, "%s|%d|%d|%d", acPreStat[g_ucPreStat], 
                                               g_uiMaxTraffic,
                                               g_uiTraffic,
                                               g_ucSignal);
        iRet = xsXmppSendPre(&s_stxsCtx, 0, (unsigned char *)acPreStateBuff);
        if(XS_OK == iRet){
          set_xc_timing_delay(60000);
          s_usLastPerState = g_ucPreStat;
        }
        break;
      }

      if(0 != get_xc_timing_delay()){
				break;
			}
      memset(acPreStateBuff, 0, sizeof(acPreStateBuff));
      sprintf(acPreStateBuff, "%s|%d|%d|%d", acPreStat[g_ucPreStat], 
                                             g_uiMaxTraffic,
                                             g_uiTraffic,
                                             g_ucSignal);
			iRet = xsXmppSendPre(&s_stxsCtx, 0, (unsigned char *)acPreStateBuff);
      if(XS_OK == iRet){
        set_xc_timing_delay(60000); /* 3600000 == 1 hour */
      }
			break;
    case P_GET_CFG:
      if(0 != get_xc_timing_delay()){
				break;
			}
      
      if(0 == s_stUserCfg.acDevPeer[0]){
        g_ucPreStat = XS_NO_CFG;
        iRet = xsXmppSendcmd(&s_stxsCtx, s_stUserCfg.acMasterJID, XS_GET_CFG, acPreStat[g_ucPreStat]);
        if(XS_OK == iRet){
          set_xc_timing_delay(10000);
        }
      }else{
        g_ucXcStep = P_HEARBEAT;
        g_ucPreStat = XS_PRE_NORMAL;
      }
      break;
    case P_CMD_RES:
      iRet = xsXmppSendRes(&s_stxsCtx, 0, g_ucCmdStat);
      if(XS_OK == iRet){
        g_ucXcStep = P_HEARBEAT;
      }
      break;
		case P_LOGOUT:
			xsXmppLogout(&s_stxsCtx);
			s_ucP2pStat = P2P_FAIL;
			g_ucXcStep = P_RELEASE;
			break;
		case P_RELEASE:
      s_ucP2pStat = P2P_FAIL;
			g_ucXcStep = P_TCP_CONNECT;
      memset(&g_stTCPRbuf, 0, sizeof(xsNetSR_t));
      set_xc_timing_delay(XC_CONNECT_TIMEOUT);
			xc_socket_close();
      tcp_and_udp_init();
			break;
	}
	return s_ucP2pStat;
}

